float4x4 World;
float4x4 ViewProjection;
float4x4 LightViewProjection;
float3 LightDir;
float DepthBias;

Texture ShadowMap;
sampler ShadowMapSampler = sampler_state {
	texture = <ShadowMap>;
	filter=MIN_MAG_MIP_POINT;
	AddressU = Clamp;
	AddressV = Clamp;
};

Texture Texture1;
sampler Texture1Sampler = sampler_state {
	texture = <Texture1>;
	filter=MIN_MAG_MIP_LINEAR;
	AddressU = Wrap;
	AddressV = Wrap;
};

Texture Texture2;
sampler Texture2Sampler = sampler_state {
	texture = <Texture2>;
	filter=MIN_MAG_MIP_LINEAR;
	AddressU = Wrap;
	AddressV = Wrap;
};

Texture Texture3;
sampler Texture3Sampler = sampler_state {
	texture = <Texture3>;
	filter=MIN_MAG_MIP_LINEAR;
	AddressU = Wrap;
	AddressV = Wrap;
};

struct VertexShaderInput
{
    float4 Position : POSITION0;
	float3 Normal : NORMAL0;
	float2 TexCoord : TEXCOORD0;
};

struct ShadowedVSOutput
{
    float4 Position : POSITION0;
    float4 Pos2DAsSeenByLight : TEXCOORD0;
	float3 Normal : TEXCOORD1;
	float4 Position3D : TEXCOORD2;
    float2 TexCoord : TEXCOORD3; 
};


struct TerrainVSInput
{
    float4 Position : POSITION0;
	float3 Normal : NORMAL0;
	float2 TexCoord : TEXCOORD0;
	float3 TexWeight : TEXCOORD1;
};

struct TerrainShadowedVSOutput
{
    float4 Position : POSITION0;
    float4 Pos2DAsSeenByLight : TEXCOORD0;
	float3 Normal : TEXCOORD1;
	float4 Position3D : TEXCOORD2;
    float2 TexCoord : TEXCOORD3; 
	float3 TexWeight : TEXCOORD4;
};

struct DiffusedVSOutput
{
    float4 Position : POSITION0;
	float3 Normal : TEXCOORD0;
    float2 TexCoord : TEXCOORD1; 
};

struct CreateShadowVSOutput
{
    float4 Position : POSITION0;
    float Depth : TEXCOORD0;
};

struct WaterVSInput
{
    float4 Position : POSITION0;
	float2 TexCoord : TEXCOORD0;
};

struct WaterVSOutput
{
    float4 Position : POSITION0;
	float2 TexCoord : TEXCOORD0;
};

/// Textur und Schatten
ShadowedVSOutput ShadowedVS(VertexShaderInput input)
{
    ShadowedVSOutput output;

    float4x4 preWorldViewProjection = mul(World, ViewProjection);
    float4x4 preLightWorldViewProjection = mul (World, LightViewProjection);

    output.Position = mul(input.Position, preWorldViewProjection);
	output.Position3D = mul(input.Position, World);
    output.Pos2DAsSeenByLight = mul(input.Position, preLightWorldViewProjection);
	output.Normal = normalize(mul(input.Normal,(float3x3)World)); // normale Drehen
	output.TexCoord = input.TexCoord;
    return output;
}

float4 ShadowedPS(ShadowedVSOutput input) : COLOR0
{
	float4 color = tex2D(Texture1Sampler, input.TexCoord);

    float2 ProjectedTexCoords;
    ProjectedTexCoords[0] = input.Pos2DAsSeenByLight.x/input.Pos2DAsSeenByLight.w/2.0f +0.5f;
    ProjectedTexCoords[1] = -input.Pos2DAsSeenByLight.y/input.Pos2DAsSeenByLight.w/2.0f +0.5f;

    float diffuseLightingFactor = .1f;
    if ((saturate(ProjectedTexCoords).x == ProjectedTexCoords.x) && (saturate(ProjectedTexCoords).y == ProjectedTexCoords.y))
    {
        float depthStoredInShadowMap = tex2D(ShadowMapSampler, ProjectedTexCoords).r;
        float realDistance = 1 - (input.Pos2DAsSeenByLight.z/input.Pos2DAsSeenByLight.w);
        if (depthStoredInShadowMap - DepthBias <= realDistance )
        {
            diffuseLightingFactor += saturate(dot(input.Normal,-LightDir));
        }
    }

    color.rgb *= diffuseLightingFactor;
    return color;
}


/// Terrain Texture
TerrainShadowedVSOutput TerrainShadowedVS(TerrainVSInput input)
{
    TerrainShadowedVSOutput output;

    float4x4 preWorldViewProjection = mul(World, ViewProjection);
    float4x4 preLightWorldViewProjection = mul (World, LightViewProjection);

    output.Position = mul(input.Position, preWorldViewProjection);
	output.Position3D = mul(input.Position, World);
    output.Pos2DAsSeenByLight = mul(input.Position, preLightWorldViewProjection);
	output.Normal = normalize(mul(input.Normal,(float3x3)World)); // normale Drehen
	output.TexCoord = input.TexCoord;
	output.TexWeight = input.TexWeight;
    return output;
}

float4 TerrainShadowedPS(TerrainShadowedVSOutput input) : COLOR0
{
	float4 color = tex2D(Texture1Sampler, input.TexCoord) * input.TexWeight.x;
	color += tex2D(Texture2Sampler, input.TexCoord) * input.TexWeight.y;
	color += tex2D(Texture3Sampler, input.TexCoord * 5) * input.TexWeight.z; // float2((input.Position3D.x+input.Position3D.z)*.1,input.Position3D.y)) * input.TexWeight.z;

    float2 ProjectedTexCoords;
    ProjectedTexCoords[0] = input.Pos2DAsSeenByLight.x/input.Pos2DAsSeenByLight.w/2.0f +0.5f;
    ProjectedTexCoords[1] = -input.Pos2DAsSeenByLight.y/input.Pos2DAsSeenByLight.w/2.0f +0.5f;

    float diffuseLightingFactor = .1f;
    if ((saturate(ProjectedTexCoords).x == ProjectedTexCoords.x) && (saturate(ProjectedTexCoords).y == ProjectedTexCoords.y))
    {
        float depthStoredInShadowMap = tex2D(ShadowMapSampler, ProjectedTexCoords).r;
        float realDistance = 1 - (input.Pos2DAsSeenByLight.z/input.Pos2DAsSeenByLight.w);
        if (depthStoredInShadowMap - DepthBias <= realDistance )
        {
            diffuseLightingFactor += saturate(dot(input.Normal,-LightDir));
        }
    }

    color.rgb *= diffuseLightingFactor;
    return color;
}


/// Texturn
DiffusedVSOutput DiffusedVS(VertexShaderInput input)
{
    DiffusedVSOutput output;

    float4x4 preWorldViewProjection = mul(World, ViewProjection);

    output.Position = mul(input.Position, preWorldViewProjection);
	output.Normal = normalize(mul(input.Normal,(float3x3)World)); // normale Drehen
	output.TexCoord = input.TexCoord;
    return output;
}

float4 DiffusedPS(DiffusedVSOutput input) : COLOR0
{
	float4 color = tex2D(Texture1Sampler, input.TexCoord);
    float diffuseLightingFactor = saturate(dot(input.Normal,-LightDir)) + .1f;
    color.rgb *= diffuseLightingFactor;
    return color;
}

/// Water
WaterVSOutput WaterVS(WaterVSInput input)
{
    WaterVSOutput output;

    float4x4 preWorldViewProjection = mul(World, ViewProjection);

    output.Position = mul(input.Position, preWorldViewProjection);
	output.TexCoord = input.TexCoord;
    return output;
}

float4 WaterPS(WaterVSOutput input) : COLOR0
{
    return tex2D(Texture1Sampler, input.TexCoord);
}

/// Shatten erstellen
CreateShadowVSOutput CreateShadowVS(VertexShaderInput input)
{
    CreateShadowVSOutput output;
	
    float4x4 preLightWorldViewProjection = mul (World, LightViewProjection);

    output.Position = mul(input.Position, preLightWorldViewProjection);
    output.Depth = 1 - (output.Position.z/output.Position.w);
    return output;
}

float4 CreateShadowPS(CreateShadowVSOutput input) : COLOR
{
    return float4(input.Depth,0,0,0);
}


/// Techniken
technique Textured
{
    pass Pass1
    {
        VertexShader = compile vs_2_0 DiffusedVS();
        PixelShader = compile ps_2_0 DiffusedPS();
    }
}

technique TexturedShadowed
{
    pass Pass1
    {
        VertexShader = compile vs_2_0 ShadowedVS();
        PixelShader = compile ps_2_0 ShadowedPS();
    }
}

technique TexturedShadowedTerrain
{
    pass Pass1
    {
        VertexShader = compile vs_2_0 TerrainShadowedVS();
        PixelShader = compile ps_2_0 TerrainShadowedPS();
    }
}

technique TexturedTerrain
{
    pass Pass1
    {
        VertexShader = compile vs_2_0 DiffusedVS();
        PixelShader = compile ps_2_0 DiffusedPS();
    }
}

technique Water
{
    pass Pass1
    {
        AlphaBlendEnable = TRUE;
        DestBlend = INVSRCALPHA;
        SrcBlend = SRCALPHA;
        VertexShader = compile vs_2_0 WaterVS();
        PixelShader = compile ps_2_0 WaterPS();
    }
}

technique CreateShadow
{
    pass Pass1
    {
		CullMode = NONE;
		ZEnable = TRUE;
		ZWriteEnable = TRUE;
		AlphaBlendEnable = FALSE;
        VertexShader = compile vs_2_0 CreateShadowVS();
        PixelShader = compile ps_2_0 CreateShadowPS();
    }
}